#!/usr/bin/env python

import display
try:
    from gi.repository import Gtk
    from gi.repository import GObject
    assert display.gtk_version() == 3
except:
    import gtk as Gtk
    import gobject as GObject
    assert display.gtk_version() == 2
import os
from config import Config, WindowSize
from status import StatusWindow, StatusDetail, StatusTray
from project import Project, ProjectCache
from threading import Thread



class Barometer:

    def __init__(self):
        #initialise components
        self.config = Config()
        self.status_window = StatusWindow()
        self.status_tray = StatusTray()
        self.project_cache = ProjectCache(self.config)

        #initialise threading
        print("GObject threads_init")
        GObject.threads_init()

        #load projects
        for project_directory in self.config.project_directories:
            print("Recovered " + project_directory)
            self.add_project(project_directory)

        #connect icon signals
        self.status_tray.show_item.connect("activate", self.show_projects)
        self.status_tray.refresh_now_item.connect("activate", self.refresh_all_projects)
        self.status_tray.quit_item.connect("activate", self.quit)

        #connect window signals
        self.status_window.add_button.connect("clicked", self.choose_project)
        self.status_window.remove_button.connect("clicked", self.remove_project)
        self.status_window.refresh_button.connect("clicked", self.refresh_project)
        self.status_window.info_button.connect("clicked", self.show_project_detail)
        self.status_window.auto.connect("toggled", self.toggle_auto)
        self.status_window.interval.connect("value-changed", self.change_interval)
        self.status_window.window.connect("size-allocate", self.resize_status_window)

        #create timeout
        if self.config.settings.auto:
            print("Starting timeout")
            self.start_timeout()
        for project in self.project_cache.get_projects():
            self.start_background(project)

        #start gtk main loop
        Gtk.main()


    #refreshing timeouts and idles
    def start_timeout(self):
        self.continue_timeout = True
        minutes = int(self.config.settings.interval * 1000 * 60)
        GObject.timeout_add(minutes, self.projects_refresher)

    def stop_timeout(self):
        self.continue_timeout = False

    def start_background(self, project):
        self.project_refresher(project)


    def projects_refresher(self):
        print("Refreshing projects...")
        for project in self.project_cache.get_projects():
            self.project_refresher(project)
        return self.continue_timeout

    #project refresher method
    def project_refresher(self, project):
        print("Refreshing " + project.name)
        refresher_thread = RefresherThread(project, self)
        refresher_thread.start()
        return False

    def project_updator(self, project):
        self.status_window.update_project(project)
        self.count_unclean()

    #count unclean projects
    def count_unclean(self):
        count = 0
        for project in self.project_cache.get_projects():
            if not project.is_clean():
                count += 1
        self.status_tray.update(count)


    #icon callback
    def show_projects(self, caller):
        self.status_window.show(self.config.settings)

    #add button callback
    def choose_project(self, button):
        search_directory = self.select_directory()
        if search_directory:
            project_directories = []
            if self.contains_repository(search_directory):
                project_directories.append(search_directory)
            for root, dirnames, filenames in os.walk(search_directory):
                for dirname in dirnames:
                    project_directory = os.path.join(root, dirname)
                    if self.contains_repository(project_directory):
                        project_directories.append(project_directory)
            if project_directories:
                for project_directory in project_directories:
                    project = self.add_project(project_directory)
                    self.start_background(project)
            else:
                self.error_dialog("No new projects found under " + search_directory)


    #chooser dialog
    def select_directory(self):
        chooser, ok, cancel = display.load_chooser(self.status_window.window)
        response = chooser.run()
        if response == ok:
            project_directory = chooser.get_filename()
        elif response == cancel:
            project_directory = None
        chooser.destroy()
        return project_directory

    def contains_repository(self, project_directory):
       scm_directory = os.path.join(project_directory, ".hg")
       return os.path.exists(scm_directory) and not self.project_cache.has_project(project_directory)


    #project adder
    def add_project(self, project_directory):
        print("Adding " + project_directory)
        project = Project(project_directory)
        self.project_cache.add_project(project)
        self.status_window.add_project(project)
        return project

    #extract project out of cache
    def get_selected_project(self):
        project = None    
        model, treeiter = self.status_window.selection.get_selected()
        if treeiter != None and model != None:
            path = model[treeiter][4]
            project = self.project_cache.get_project(path)
        return project, treeiter

    #remove button callback
    def remove_project(self, button):
        project, treeiter = self.get_selected_project()
        if project != None:
            self.project_cache.remove_project(project)
            self.status_window.remove_project(treeiter)
        else:
            self.error_dialog("Select a project to remove")
        self.count_unclean()

    #refresh now menu callback
    def refresh_all_projects(self, caller):
        for project in self.project_cache.get_projects():
            self.start_background(project)


    #refresh button callback
    def refresh_project(self, button):
        project, treeiter = self.get_selected_project()
        if project != None:
            self.start_background(project)
        else:
            self.error_dialog("Select a project to refresh")
        self.count_unclean()

    #detail button callback
    def show_project_detail(self, button):
        project, treeiter = self.get_selected_project()
        if project != None:
            print("Showing detail for " + project.name) 
            status_detail = StatusDetail(project, self.config.settings)
            status_detail.window.connect("size-allocate", self.resize_detail_window)
            status_detail.pane.connect("notify::position", self.resize_detail_pane)
        else:
            self.error_dialog("Select a project to detail")

    #auto callback
    def toggle_auto(self, toggle):
        auto = toggle.get_active()
        if self.config.settings.auto != auto:
            self.config.settings.auto = auto
            if auto:
                self.start_timeout()
            else:
                self.stop_timeout()
            self.config.persist_settings()

    #interval callback
    def change_interval(self, spinner):
        interval = spinner.get_value()
        if self.config.settings.interval != interval:
            self.config.settings.interval = interval
            self.stop_timeout()
            self.start_timeout()
            self.config.persist_settings()

    #size persistance callbacks
    def resize_status_window(self, window, rectangle):
        window_size = WindowSize(rectangle.width, rectangle.height)
        self.config.settings.status_size = window_size
        self.config.persist_settings()

    def resize_detail_window(self, window, rectangle):
        window_size = WindowSize(rectangle.width, rectangle.height)
        self.config.settings.detail_size = window_size
        self.config.persist_settings()

    def resize_detail_pane(self, pane, parameter):
        self.config.settings.detail_pane = pane.get_position()
        self.config.persist_settings()

    #error dialog displayer
    def error_dialog(self, text):
        dialog = display.load_error_dialog(self.status_window.window, text)
        dialog.run()
        dialog.destroy()

    #quit main loop
    def quit(self, caller):
        Gtk.main_quit()


class RefresherThread(Thread):
    def __init__(self, project, barometer):
        Thread.__init__(self)
        self.project = project
        self.barometer = barometer

    def run(self):
        self.project.refresh()
        GObject.idle_add(self.barometer.project_updator, self.project)

#script entry point
if __name__ == "__main__":
    Barometer()

